package org.atovio.structure.application;

import org.atovio.structure.tree.HuffmanTree;

import java.io.*;

/**
 * 依据Huffman树实现的文件压缩类。
 * @author AtoVio, Winter Yuan
 * @version 1.0
 */
public class FileCompress implements Serializable{

    @Serial
    private static final long serialVersionUID = 1L;
    // 字符集
    public String charset;
    // 权重集合
    public int[] weights;
    // Huffman树
    public HuffmanTree tree;

    /**
     * 根据指定文件生成Huffman树。并根据文件内容生成唯一的权重和字符集
     * @param originFile 原文件
     */
    public FileCompress(File originFile){
        this.charset = makeCharset(originFile);
        this.weights = makeWeights(originFile);
        tree = new HuffmanTree(this.charset, this.weights);
    }

    /**
     * 设置文件的字符集
     *
     * @param file 待压缩文件
     */
    public String makeCharset(File file) {
        StringBuilder str = new StringBuilder();
        try (FileInputStream fis = new FileInputStream(file)) {
            int readData;
            while ((readData = fis.read()) != -1) {
                char temp = ((char) readData);
                if (str == null || str.toString().indexOf(temp) == -1)
                    str.append(temp);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return str.toString();
    }

    /**
     * 设置权重集合
     *
     * @param file 待压缩文件
     */
    public int[] makeWeights(File file) {
        int n = this.charset.length();
        int[] weights = new int[n];
        try (FileInputStream fis = new FileInputStream(file)) {
            int readData;
            while ((readData = fis.read()) != -1) {
                char temp = ((char) readData);
                weights[charset.indexOf(temp)]++;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return weights;
    }

    /**
     * 压缩文件，获取文件的Huffman编码
     * @param originFile 原文件
     * @param compressedFile 压缩后的文件
     */
    public void encode(File originFile, File compressedFile) {
        StringBuilder str = new StringBuilder();
        try (FileInputStream fis = new FileInputStream(originFile)) {
            int readData;
            while ((readData = fis.read()) != -1) {
                char temp = ((char) readData);
                str.append(this.tree.huffmanCode(charset.indexOf(temp)));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(compressedFile))){
            writer.write(str.toString());
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 解压缩文件。
     * @param originFile 解压缩后的文件
     * @param compressedFile 压缩文件
     */
    public void decode(File compressedFile, File originFile) {
        StringBuilder str = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(new FileReader(compressedFile))){
            String temp = reader.readLine();
            str.append(this.tree.decode(temp));
        } catch (IOException e) {
            e.printStackTrace();
        }
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(originFile))) {
            writer.write(str.toString());
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
